#include "hr_tbr.hpp"
#ifndef __SYNTHESIS__
#include <iostream>
#include <algorithm> // For std::min/max in C-simulation
#include <iomanip>  // For std::fixed, std::setprecision
#endif

// 辅助函数：更新Bounding Box (像素坐标)
void update_bounding_box_pixels(
    fpal::fp_coord_t vx, fpal::fp_coord_t vy,
    fpal::fp_screen_pixel_idx_t& min_x_px, fpal::fp_screen_pixel_idx_t& max_x_px,
    fpal::fp_screen_pixel_idx_t& min_y_px, fpal::fp_screen_pixel_idx_t& max_y_px
) {
    #pragma HLS INLINE
    fpal::fp_screen_pixel_idx_t cur_x_idx = fpal::to_fp_screen_pixel_idx(vx);
    fpal::fp_screen_pixel_idx_t cur_y_idx = fpal::to_fp_screen_pixel_idx(vy);
    
    // Using simple if conditions instead of std::min/max for HLS compatibility
    if (cur_x_idx < min_x_px) min_x_px = cur_x_idx;
    if (cur_x_idx > max_x_px) max_x_px = cur_x_idx;
    if (cur_y_idx < min_y_px) min_y_px = cur_y_idx;
    if (cur_y_idx > max_y_px) max_y_px = cur_y_idx;
}


// ===========================================================
// HR_TBR_Binning 模块实现 (Triangle Binning)
// ===========================================================
void hr_tbr_binning_with_bram_write(
    hls::stream<raster_params_packet_fixed>& input_raster_params_stream,
    binned_triangle_packet_fixed all_triangles_bram[MAX_NUM_TRIANGLES], // BRAM 数组
    hls::stream<ap_uint<11>>& s_max_x_res_in,
    hls::stream<ap_uint<11>>& s_max_y_res_in,
    hls::stream<ap_uint<16>>& s_num_triangles_to_process_in,
    hls::stream<TileListDataPacket>& s_tile_list_out,
    hls::stream<ap_uint<11>>& s_max_x_res_out,
    hls::stream<ap_uint<11>>& s_max_y_res_out,
    hls::stream<ap_uint<16>>& s_num_triangles_to_process_out // 输出流
) {
    #pragma HLS INLINE OFF // 这是一个 DATAFLOW 阶段，其自身不应被内联

    // 在此函数内部声明并绑定这些 BRAM
    static ap_uint<TILE_LIST_MAX_TRIANGLES_PER_TILE_LOG2> tile_list_count[MAX_NUM_TILES_Y][MAX_NUM_TILES_X];
    #pragma HLS bind_storage variable=tile_list_count type=ram_t2p impl=bram

    static ap_uint<TRIANGLE_ID_BITS> tile_list_data[MAX_NUM_TILES_Y][MAX_NUM_TILES_X][TILE_LIST_MAX_TRIANGLES_PER_TILE];
    #pragma HLS bind_storage variable=tile_list_data type=ram_t2p impl=bram
    #pragma HLS bind_storage variable=all_triangles_bram type=ram_t2p impl=bram
    

    // --- 接收流式参数 ---
    const ap_uint<16> num_triangles_to_process = s_num_triangles_to_process_in.read();
    const ap_uint<11> max_x_res = s_max_x_res_in.read();
    const ap_uint<11> max_y_res = s_max_y_res_in.read();

    // --- 转发流式参数给下一个模块 ---
    s_max_x_res_out.write(max_x_res);
    s_max_y_res_out.write(max_y_res);
    s_num_triangles_to_process_out.write(num_triangles_to_process); // 转发

    // --- 在 DATAFLOW 进程内部初始化 Tile List count ---
    // 这将确保所有对 tile_list_count 的写入都由这个进程控制。
    ap_uint<8> num_tiles_x_actual = (max_x_res + TILE_WIDTH - 1) / TILE_WIDTH;
    ap_uint<8> num_tiles_y_actual = (max_y_res + TILE_HEIGHT - 1) / TILE_HEIGHT;

    TILE_LIST_INIT_Y:
    for (ap_uint<8> ty = 0; ty < num_tiles_y_actual; ++ty) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y // TRIPCOUNT 修正
        TILE_LIST_INIT_X:
        for (ap_uint<8> tx = 0; tx < num_tiles_x_actual; ++tx) {
            #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X // TRIPCOUNT 修正
            #pragma HLS PIPELINE II=1
            tile_list_count[ty][tx] = 0;
            #ifndef __SYNTHESIS__
            // 新增调试：初始化时打印 tile_list_count
            if (ty == 0 && tx == 0) {
                 std::cout << "[HR_TBR_DEBUG] Initializing tile_list_count[" << (int)ty << "][" << (int)tx << "] to " << (int)tile_list_count[ty][tx] << std::endl;
            }
            #endif
        }
    }
    // -------------------------------------------------------------


    TRIANGLE_BINNING_LOOP:
    for (ap_uint<16> triangle_idx = 0; triangle_idx < num_triangles_to_process; ++triangle_idx) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TRIANGLES 

        raster_params_packet_fixed current_triangle_params;
        input_raster_params_stream.read(current_triangle_params);

        fpal::fp_coord_t v0_x_fx = current_triangle_params.v0_x;
        fpal::fp_coord_t v0_y_fx = current_triangle_params.v0_y;
        fpal::fp_coord_t v1_x_fx = current_triangle_params.v1_x;
        fpal::fp_coord_t v1_y_fx = current_triangle_params.v1_y;
        fpal::fp_coord_t v2_x_fx = current_triangle_params.v2_x;
        fpal::fp_coord_t v2_y_fx = current_triangle_params.v2_y;

        // ======================== 新增调试代码 (打印 v0_x_fx, v0_y_fx 的原始内部值) ========================
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) { // 只打印前5个三角形
            std::cout << "[HR_TBR_DEBUG_V_FX_PRE_CONV] Triangle " << (int)triangle_idx << " v0_x_fx (internal value): " 
                      << v0_x_fx.to_string(10) << " (hex: " << v0_x_fx.to_string(16) << ")" << std::endl;
            std::cout << "[HR_TBR_DEBUG_V_FX_PRE_CONV] Triangle " << (int)triangle_idx << " v0_y_fx (internal value): " 
                      << v0_y_fx.to_string(10) << " (hex: " << v0_y_fx.to_string(16) << ")" << std::endl;
        }
        #endif
        // =================================================================================================

        // Calculate Pixel-level Bounding Box
        fpal::fp_screen_pixel_idx_t min_x_px_raw = fpal::to_fp_screen_pixel_idx(v0_x_fx); // 获取原始转换结果
        fpal::fp_screen_pixel_idx_t min_y_px_raw = fpal::to_fp_screen_pixel_idx(v0_y_fx); // 获取原始转换结果

        // ======================== 新增调试代码 (打印 to_fp_screen_pixel_idx 的原始输出) ========================
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) { // 只打印前5个三角形
            std::cout << "[HR_TBR_DEBUG_PX_RAW_AFTER_CONV] Triangle " << (int)triangle_idx << " Raw Px Index (from v0):" << std::endl;
            std::cout << "  min_x_px (raw from to_fp_screen_pixel_idx): " << (int)min_x_px_raw << std::endl;
            std::cout << "  min_y_px (raw from to_fp_screen_pixel_idx): " << (int)min_y_px_raw << std::endl;
        }
        #endif
        // =======================================================================================================
        
        fpal::fp_screen_pixel_idx_t min_x_px = min_x_px_raw; // 初始化
        fpal::fp_screen_pixel_idx_t max_x_px = min_x_px;
        fpal::fp_screen_pixel_idx_t min_y_px = min_y_px_raw;
        fpal::fp_screen_pixel_idx_t max_y_px = min_y_px;

        // ======================== 更新调试代码 (显示初始化后的 min/max_x/y_px) ========================
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) {
            std::cout << "[HR_TBR_DEBUG_PX_INIT] Triangle " << (int)triangle_idx << " Initial Px BB (initialized with v0 converted coords):" << std::endl;
            std::cout << "  min_x_px (init): " << (int)min_x_px << std::endl;
            std::cout << "  max_x_px (init): " << (int)max_x_px << std::endl; // 此时 max_x_px == min_x_px
            std::cout << "  min_y_px (init): " << (int)min_y_px << std::endl;
            std::cout << "  max_y_px (init): " << (int)max_y_px << std::endl; // 此时 max_y_px == min_y_px
        }
        #endif
        // =================================================================================================

        update_bounding_box_pixels(v1_x_fx, v1_y_fx, min_x_px, max_x_px, min_y_px, max_y_px);
        update_bounding_box_pixels(v2_x_fx, v2_y_fx, min_x_px, max_x_px, min_y_px, max_y_px);
        
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) {
            std::cout << "[HR_TBR_DEBUG_PX_UPDATED] Triangle " << (int)triangle_idx << " Px BB after update_bounding_box_pixels (v1 & v2):" << std::endl;
            std::cout << "  min_x_px: " << (int)min_x_px << ", max_x_px: " << (int)max_x_px << std::endl;
            std::cout << "  min_y_px: " << (int)min_y_px << ", max_y_px: " << (int)max_y_px << std::endl;
        }
        #endif

        fpal::fp_screen_pixel_idx_t screen_max_x_idx = max_x_res - 1;
        fpal::fp_screen_pixel_idx_t screen_max_y_idx = max_y_res - 1;

        // Using simple if conditions for clamp instead of std::min/max for HLS compatibility
        if (min_x_px < 0) min_x_px = 0;
        if (min_x_px > screen_max_x_idx) min_x_px = screen_max_x_idx;
        if (max_x_px < 0) max_x_px = 0;
        if (max_x_px > screen_max_x_idx) max_x_px = screen_max_x_idx;

        if (min_y_px < 0) min_y_px = 0;
        if (min_y_px > screen_max_y_idx) min_y_px = screen_max_y_idx;
        if (max_y_px < 0) max_y_px = 0;
        if (max_y_px > screen_max_y_idx) max_y_px = screen_max_y_idx;

        // Ensure min <= max (this handles cases where bounding box is invalid, e.g., triangle outside screen)
        if (min_x_px > max_x_px) max_x_px = min_x_px; // Or swap them, depending on desired behavior
        if (min_y_px > max_y_px) max_y_px = min_y_px; // Or swap them

        // Calculate Tile-level Bounding Box
        ap_uint<8> min_tile_x = min_x_px / TILE_WIDTH;
        ap_uint<8> max_tile_x = max_x_px / TILE_WIDTH;
        ap_uint<8> min_tile_y = min_y_px / TILE_HEIGHT;
        ap_uint<8> max_tile_y = max_y_px / TILE_HEIGHT;

        ap_uint<8> max_possible_tile_x_idx = num_tiles_x_actual - 1;
        ap_uint<8> max_possible_tile_y_idx = num_tiles_y_actual - 1;

        // Clamping tile coordinates
        if (min_tile_x < 0) min_tile_x = 0;
        if (min_tile_x > max_possible_tile_x_idx) min_tile_x = max_possible_tile_x_idx;
        if (max_tile_x < 0) max_tile_x = 0;
        if (max_tile_x > max_possible_tile_x_idx) max_tile_x = max_possible_tile_x_idx;

        if (min_tile_y < 0) min_tile_y = 0;
        if (min_tile_y > max_possible_tile_y_idx) min_tile_y = max_possible_tile_y_idx;
        if (max_tile_y < 0) max_tile_y = 0;
        if (max_tile_y > max_possible_tile_y_idx) max_tile_y = max_possible_tile_y_idx;

        // Populate the binned_triangle_packet_fixed structure
        binned_triangle_packet_fixed binned_packet;
        binned_packet.v0_x_ref = v0_x_fx;
        binned_packet.v0_y_ref = v0_y_fx;
        binned_packet.v0_z_ref = current_triangle_params.v0_z;
        binned_packet.edge0_A = current_triangle_params.edge0_A; binned_packet.edge0_B = current_triangle_params.edge0_B; binned_packet.edge0_C = current_triangle_params.edge0_C;
        binned_packet.edge1_A = current_triangle_params.edge1_A; binned_packet.edge1_B = current_triangle_params.edge1_B; binned_packet.edge1_C = current_triangle_params.edge1_C;
        binned_packet.edge2_A = current_triangle_params.edge2_A; binned_packet.edge2_B = current_triangle_params.edge2_B; binned_packet.edge2_C = current_triangle_params.edge2_C;
        binned_packet.dzdx = current_triangle_params.dzdx;
        binned_packet.dzdy = current_triangle_params.dzdy;
        
        // --- 修改开始 ---
        // 打包颜色分量
        binned_packet.packed_pure_color = ( (ap_uint<24>)current_triangle_params.pure_color_R << 16) |
                                          ( (ap_uint<24>)current_triangle_params.pure_color_G << 8)  |
                                          ( (ap_uint<24>)current_triangle_params.pure_color_B << 0);
        // 为了 C 仿真调试方便，赋值回 R/G/B 成员
        binned_packet.pure_color_R = current_triangle_params.pure_color_R;
        binned_packet.pure_color_G = current_triangle_params.pure_color_G;
        binned_packet.pure_color_B = current_triangle_params.pure_color_B;
        // --- 修改结束 ---

        binned_packet.min_x_px = min_x_px; binned_packet.max_x_px = max_x_px;
        binned_packet.min_y_px = min_y_px; binned_packet.max_y_px = max_y_px;
        binned_packet.min_tile_x = min_tile_x; binned_packet.max_tile_x = max_tile_x;
        binned_packet.min_tile_y = min_tile_y; binned_packet.max_tile_y = max_tile_y;
        
        binned_packet.is_valid_triangle = (min_x_px <= max_x_px && min_y_px <= max_y_px);

        // ======================== 现有调试代码 (计算 Tile Bounding Box 后) ========================
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) { // 只打印前5个三角形
            std::cout << "[HR_TBR_DEBUG_BIN_CALC] Triangle " << (int)triangle_idx << " Binned Info (after pixel-level clamping):" << std::endl;
            std::cout << "  min_x_px: " << (int)min_x_px << ", max_x_px: " << (int)max_x_px << std::endl;
            std::cout << "  min_y_px: " << (int)min_y_px << ", max_y_px: " << (int)max_y_px << std::endl;
            std::cout << "  min_tile_x: " << (int)min_tile_x << ", max_tile_x: " << (int)max_tile_x << std::endl;
            std::cout << "  min_tile_y: " << (int)min_tile_y << ", max_tile_y: " << (int)max_tile_y << std::endl;
            std::cout << "  is_valid_triangle: " << (binned_packet.is_valid_triangle ? "true" : "false") << std::endl;
        }
        #endif
        // ======================================================================================

        // Store the binned triangle in the BRAM array
        all_triangles_bram[triangle_idx] = binned_packet;

        // ======================== 现有调试代码 (HR_TBR 写入 BRAM 后颜色检查) ========================
        #ifndef __SYNTHESIS__
        if (triangle_idx < 5) { // 只打印前5个三角形
            std::cout << "[HR_TBR_DEBUG] Triangle " << (int)triangle_idx << " after writing to all_triangles_bram:" << std::endl;
            std::cout << "  packed_pure_color (BRAM, hex): 0x" << std::hex << std::setw(6) << std::setfill('0') << (long long)all_triangles_bram[triangle_idx].packed_pure_color << std::dec << std::endl;
            std::cout << "  pure_color_R (BRAM): " << (int)all_triangles_bram[triangle_idx].pure_color_R << std::endl;
            std::cout << "  pure_color_G (BRAM): " << (int)all_triangles_bram[triangle_idx].pure_color_G << std::endl;
            std::cout << "  pure_color_B (BRAM): " << (int)all_triangles_bram[triangle_idx].pure_color_B << std::endl;
        }
        #endif
        // ==============================================================================

        // Update the Tile List Buffer if the triangle is valid
        if (binned_packet.is_valid_triangle) {
            TILE_BINNING_X_LOOP:
            for (ap_uint<8> tile_x = min_tile_x; tile_x <= max_tile_x; ++tile_x) {
                #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X
                TILE_BINNING_Y_LOOP:
                for (ap_uint<8> tile_y = min_tile_y; tile_y <= max_tile_y; ++tile_y) {
                    #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y

                    ap_uint<TILE_LIST_MAX_TRIANGLES_PER_TILE_LOG2> current_count = tile_list_count[tile_y][tile_x];
                    
                    // Add triangle ID if space is available
                    if (current_count < TILE_LIST_MAX_TRIANGLES_PER_TILE) {
                        tile_list_data[tile_y][tile_x][current_count] = triangle_idx; // <-- 关键写入点
                        tile_list_count[tile_y][tile_x] = current_count + 1;
                        #ifndef __SYNTHESIS__
                        // 现有调试：打印 Tile List 数据写入
                        if (tile_y == 0 && tile_x == 0 && current_count < 5) { // 仅关注 Tile (0,0) 的前几个槽位
                            std::cout << "[HR_TBR_DEBUG_BINNING] Triangle " << (int)triangle_idx 
                                      << " written to tile_list_data[" << (int)tile_y << "][" << (int)tile_x << "][" 
                                      << (int)current_count << "]. New count: " << (int)(current_count + 1) << std::endl;
                        }
                        #endif
                    }
                }
            }
        }
    } // end TRIANGLE_BINNING_LOOP

    // 在 TRIANGLE_BINNING_LOOP 结束后，将 BRAM 内容流化出去 (此处是将 tile_list_data 流化出去, all_triangles_bram 是直接共享的)
    FORWARD_TILE_LIST_Y_LOOP:
    for (ap_uint<8> ty = 0; ty < num_tiles_y_actual; ++ty) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y // TRIPCOUNT 修正
        FORWARD_TILE_LIST_X_LOOP:
        for (ap_uint<8> tx = 0; tx < num_tiles_x_actual; ++tx) {
            #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X // TRIPCOUNT 修正
            #pragma HLS PIPELINE II=1 // 强制 II=1 (确保 stream 写入无瓶颈)

            TileListDataPacket packet;
            packet.tile_x = tx;
            packet.tile_y = ty;
            packet.count = tile_list_count[ty][tx];

            // 拷贝 tile_list_data
            COPY_TRI_IDS_TO_STREAM:
            for (int k = 0; k < TILE_LIST_MAX_TRIANGLES_PER_TILE; ++k) {
                // 对于小循环，UNROLL 通常性能更好，特别是当数据可以完全并行读取时
                #pragma HLS UNROLL 
                packet.data[k] = tile_list_data[ty][tx][k];
            }
            s_tile_list_out.write(packet);

            #ifndef __SYNTHESIS__
            // 现有调试：打印流化输出的 TileListDataPacket
            if (ty == 0 && tx == 0) { // 仅关注 Tile (0,0) 的流出数据
                std::cout << "[HR_TBR_DEBUG_STREAM_OUT] TileListDataPacket for Tile (" << (int)ty << "," << (int)tx << "):" << std::endl;
                std::cout << "  Count: " << (int)packet.count << std::endl;
                std::cout << "  Triangle IDs (first 5 or up to count): ";
                for (int k = 0; k < std::min((int)packet.count, 5); ++k) {
                    std::cout << (int)packet.data[k] << " ";
                }
                std::cout << std::endl;
            }
            #endif
        }
    }
}
